import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
import logging
from datetime import datetime
import math

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('jump_analysis.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# 基础配置
pd.set_option('future.no_silent_downcasting', True)
plt.rcParams["font.family"] = ["Microsoft YaHei", "SimSun", "Arial"]
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["figure.dpi"] = 100
plt.rcParams["figure.facecolor"] = "white"


def detect_outliers(data, threshold=1.5):
    """IQR异常值检测"""
    q1 = data.quantile(0.25)
    q3 = data.quantile(0.75)
    iqr = q3 - q1
    lower_bound = q1 - threshold * iqr
    upper_bound = q3 + threshold * iqr
    return (data < lower_bound) | (data > upper_bound)


def calculate_ground_reference(df_clean, feet_y_columns, run_up_ratio=0.3):
    """计算地面基准高度和离地阈值"""
    run_up_end_idx = int(len(df_clean) * run_up_ratio)
    run_up_data = df_clean.iloc[:run_up_end_idx].copy()
    
    for col in feet_y_columns:
        outliers = detect_outliers(run_up_data[col])
        if outliers.sum() > 0:
            logger.warning(f"助跑阶段{col}检测到{outliers.sum()}个异常值，已替换为中位数")
            run_up_data.loc[outliers, col] = run_up_data[col].median()
    
    ground_mean = run_up_data[feet_y_columns].mean().mean()
    ground_std = run_up_data[feet_y_columns].std().mean()
    ground_threshold = ground_mean + 1.2 * ground_std  
    logger.info(f"离地阈值计算：{ground_mean:.2f} + 1.2×{ground_std:.2f} = {ground_threshold:.2f}")
    return ground_mean, ground_std, ground_threshold, run_up_end_idx


def calculate_joint_angle(point1, point2, point3):
    """三点法计算关节角度"""
    v1 = [point1[0] - point2[0], point1[1] - point2[1]]
    v2 = [point3[0] - point2[0], point3[1] - point2[1]]
    
    dot_product = v1[0] * v2[0] + v1[1] * v2[1]
    mag1 = math.sqrt(v1[0]**2 + v1[1]** 2)
    mag2 = math.sqrt(v2[0]**2 + v2[1]** 2)
    
    if mag1 < 1e-3 or mag2 < 1e-3:
        return 0
    
    angle_rad = math.acos(max(-1.0, min(1.0, dot_product / (mag1 * mag2))))
    return math.degrees(angle_rad)


def analyze_airborne_phase(df_clean, takeoff_idx, landing_idx, joint_matches, fps):
    """分析滞空阶段运动过程并生成描述文字"""
    # 提取滞空阶段数据
    airborne_df = df_clean.iloc[takeoff_idx:landing_idx+1].copy()
    if len(airborne_df) < 3:
        return {"status": "error", "description": "滞空阶段数据不足，无法分析"}
    
    # 1. 划分上升期和下降期（基于质心速度为0的时刻）
    apex_idx = None
    for i in range(1, len(airborne_df)-1):
        if airborne_df['v_y'].iloc[i-1] >= 0 and airborne_df['v_y'].iloc[i] <= 0:
            apex_idx = airborne_df.index[i]  # 最高点帧索引
            break
    
    # 若未找到明确顶点，用高度最大值替代
    if apex_idx is None:
        apex_idx = airborne_df['y_com'].idxmax()
    
    # 2. 计算关键参数
    takeoff_frame = df_clean.loc[takeoff_idx, '帧号']
    landing_frame = df_clean.loc[landing_idx, '帧号']
    apex_frame = df_clean.loc[apex_idx, '帧号']
    
    # 时间参数
    ascent_frames = apex_idx - takeoff_idx  # 上升帧数
    descent_frames = landing_idx - apex_idx  # 下降帧数
    ascent_time = ascent_frames / fps
    descent_time = descent_frames / fps
    
    # 高度参数
    takeoff_height = df_clean.loc[takeoff_idx, 'y_com']
    apex_height = df_clean.loc[apex_idx, 'y_com']
    landing_height = df_clean.loc[landing_idx, 'y_com']
    max_rise = apex_height - takeoff_height  # 最大上升高度
    
    # 3. 关节运动特征
    hip_movement = df_clean.loc[landing_idx, joint_matches['hip']] - df_clean.loc[takeoff_idx, joint_matches['hip']]
    wrist_movement = None
    if joint_matches['left_wrist'] and joint_matches['right_wrist']:
        left_wrist_change = df_clean.loc[landing_idx, joint_matches['left_wrist']] - df_clean.loc[takeoff_idx, joint_matches['left_wrist']]
        right_wrist_change = df_clean.loc[landing_idx, joint_matches['right_wrist']] - df_clean.loc[takeoff_idx, joint_matches['right_wrist']]
        wrist_movement = (left_wrist_change + right_wrist_change) / 2
    
    # 4. 生成运动过程描述
    description = []
    description.append(f"滞空阶段（{takeoff_frame}帧至{landing_frame}帧）共{len(airborne_df)}帧，时长{len(airborne_df)/fps:.2f}秒。")
    
    # 上升阶段描述
    if ascent_frames > 0:
        ascent_desc = (f"上升阶段从{takeoff_frame}帧至{apex_frame}帧，历时{ascent_time:.2f}秒，"
                      f"质心从{takeoff_height:.1f}像素上升至最高点{apex_height:.1f}像素，上升高度{max_rise:.1f}像素。")
        if ascent_time > descent_time * 1.2:
            ascent_desc += " 上升时间较长，可能存在起跳发力过缓。"
        elif ascent_time < descent_time * 0.8:
            ascent_desc += " 上升时间较短，起跳爆发力较强。"
        description.append(ascent_desc)
    
    # 下降阶段描述
    if descent_frames > 0:
        descent_desc = (f"下降阶段从{apex_frame}帧至{landing_frame}帧，历时{descent_time:.2f}秒，"
                       f"质心从最高点下降至落地时{landing_height:.1f}像素，下降高度{apex_height - landing_height:.1f}像素。")
        if (apex_height - landing_height) > max_rise * 1.2:
            descent_desc += " 下降幅度较大，落地时可能存在冲击较大。"
        description.append(descent_desc)
    
    # 关节运动描述
    joint_desc = []
    if hip_movement > 5:
        joint_desc.append(f"髋关节整体上升{hip_movement:.1f}像素，身体姿态保持较好。")
    elif hip_movement < -5:
        joint_desc.append(f"髋关节整体下降{abs(hip_movement):.1f}像素，可能存在落地前身体前倾。")
    else:
        joint_desc.append(f"髋关节高度变化较小（{hip_movement:.1f}像素），身体姿态稳定。")
    
    if wrist_movement is not None:
        if wrist_movement > 10:
            joint_desc.append(f"手腕整体上升明显（{wrist_movement:.1f}像素），可能存在摆臂动作充分。")
        elif wrist_movement < -10:
            joint_desc.append(f"手腕整体下降明显（{abs(wrist_movement):.1f}像素），可能存在落地前手臂前伸。")
    
    if joint_desc:
        description.append("关节运动特征：" + "；".join(joint_desc))
    
    # 5. 汇总分析结果
    return {
        "status": "success",
        "description": "\n".join(description),
        "key_params": {
            "上升时间(秒)": ascent_time,
            "下降时间(秒)": descent_time,
            "最高点帧": apex_frame,
            "最大上升高度(像素)": max_rise,
            "髋关节总变化(像素)": hip_movement,
            "手腕总变化(像素)": wrist_movement
        },
        "apex_idx": apex_idx  # 最高点索引，用于可视化
    }


def analyze_long_jump(
    file_path,
    target_sheet='Sheet1',
    takeoff_delta1=3,
    takeoff_delta2=6,
    k=2,
    landing_epsilon=3,
    acceleration_threshold=8,
    min_airborne_frames=1,
    airborne_height_threshold=2,
    max_flight_time=1.2,
    run_up_ratio=0.3,
    left_ankle='29_Y',
    right_ankle='30_Y',
    hip_candidates=['11_Y', '13_Y', '14_Y'],
    chest_candidates=['5_Y', '6_Y', '7_Y'],
    left_knee='25_Y',
    right_knee='26_Y',
    left_wrist_candidates=['15_Y', '16_Y'],
    right_wrist_candidates=['17_Y', '18_Y'],
    fps=30,
    save_results=True,
    debug=True
):
    try:
        start_time = datetime.now()
        logger.info(f"=== 开始跳远分析（时间：{start_time.strftime('%Y-%m-%d %H:%M:%S')} ====")
        
        # 1. 文件读取与关节点匹配
        file_path = Path(file_path)
        if not file_path.exists():
            raise FileNotFoundError(f"文件不存在：{file_path.resolve()}")
        
        logger.info(f"加载数据文件：{file_path.name}")
        excel_file = pd.ExcelFile(file_path)
        target_sheet = target_sheet if target_sheet in excel_file.sheet_names else excel_file.sheet_names[0]
        df = excel_file.parse(target_sheet)
        logger.info(f"读取工作表：{target_sheet}（共{len(df)}行数据，帧率{fps}fps）")
        
        # 必选列检查
        required_cols = [left_ankle, right_ankle, '帧号']
        missing_required = [col for col in required_cols if col not in df.columns]
        if missing_required:
            raise ValueError(f"缺少必选列：{missing_required}")
        
        # 自动匹配关节点
        joint_matches = {
            'hip': next((col for col in hip_candidates if col in df.columns), None),
            'chest': next((col for col in chest_candidates if col in df.columns), None),
            'left_wrist': next((col for col in left_wrist_candidates if col in df.columns), None),
            'right_wrist': next((col for col in right_wrist_candidates if col in df.columns), None)
        }
        
        if not all([joint_matches['hip'], joint_matches['chest']]):
            raise ValueError(f"核心关节点匹配失败：髋部={joint_matches['hip']}, 胸部={joint_matches['chest']}")
        logger.info(f"自动匹配关节点列：")
        logger.info(f"  - 髋部：{joint_matches['hip']}, 胸部：{joint_matches['chest']}")
        logger.info(f"  - 左手腕：{joint_matches['left_wrist']}, 右手腕：{joint_matches['right_wrist']}")
        
        # 检查X列
        all_joint_cols = [left_ankle, right_ankle, left_knee, right_knee, 
                         joint_matches['hip'], joint_matches['chest']]
        if joint_matches['left_wrist']:
            all_joint_cols.append(joint_matches['left_wrist'])
        if joint_matches['right_wrist']:
            all_joint_cols.append(joint_matches['right_wrist'])
        
        missing_x_cols = []
        for y_col in all_joint_cols:
            x_col = y_col.replace('_Y', '_X')
            if x_col not in df.columns:
                missing_x_cols.append(x_col)
        if missing_x_cols:
            raise ValueError(f"缺少X列：{missing_x_cols}")
        
        # 2. 数据预处理
        df_clean = df.copy()
        all_needed_cols = required_cols + all_joint_cols + [col.replace('_Y', '_X') for col in all_joint_cols]
        
        # 填充缺失值
        null_count = df_clean[all_needed_cols].isnull().sum().sum()
        if null_count > 0:
            logger.warning(f"检测到{null_count}个缺失值，线性插值填充")
            df_clean[all_needed_cols] = df_clean[all_needed_cols].interpolate(method='linear')
        
        # 脚部高度计算
        df_clean['h_foot_raw'] = df_clean[[left_ankle, right_ankle]].min(axis=1)
        df_clean['h_foot'] = df_clean['h_foot_raw'].ewm(span=1).mean()
        
        # 质心计算
        df_clean['y_com_raw'] = (df_clean[joint_matches['hip']] + df_clean[joint_matches['chest']]) / 2
        df_clean['y_com'] = df_clean['y_com_raw'].ewm(span=3).mean()
        
        # 起跳特征计算
        df_clean['h_foot_change'] = df_clean['h_foot'].diff()
        df_clean['delta_h_foot'] = 0.0
        valid_range = range(k, len(df_clean) - k)
        for t in valid_range:
            df_clean.loc[t, 'delta_h_foot'] = df_clean.loc[t + k, 'h_foot'] - df_clean.loc[t - k, 'h_foot']
        
        # 速度与加速度
        delta_t = 1.0 / fps
        df_clean['v_y'] = 0.0
        df_clean['a_y'] = 0.0
        for t in range(1, len(df_clean) - 1):
            df_clean.loc[t, 'v_y'] = (df_clean.loc[t + 1, 'y_com'] - df_clean.loc[t - 1, 'y_com']) / (2 * delta_t)
            if t < len(df_clean) - 2:
                df_clean.loc[t, 'a_y'] = (df_clean.loc[t + 1, 'v_y'] - df_clean.loc[t - 1, 'v_y']) / (2 * delta_t)
        
        # 膝关节角度计算
        df_clean['left_knee_angle'] = 0.0
        df_clean['right_knee_angle'] = 0.0
        for t in range(len(df_clean)):
            la = (df_clean.loc[t, '29_X'], df_clean.loc[t, '29_Y'])
            lk = (df_clean.loc[t, left_knee.replace('_Y', '_X')], df_clean.loc[t, left_knee])
            lh = (df_clean.loc[t, joint_matches['hip'].replace('_Y', '_X')], df_clean.loc[t, joint_matches['hip']])
            df_clean.loc[t, 'left_knee_angle'] = calculate_joint_angle(la, lk, lh)
            
            ra = (df_clean.loc[t, '30_X'], df_clean.loc[t, '30_Y'])
            rk = (df_clean.loc[t, right_knee.replace('_Y', '_X')], df_clean.loc[t, right_knee])
            rh = (df_clean.loc[t, joint_matches['hip'].replace('_Y', '_X')], df_clean.loc[t, joint_matches['hip']])
            df_clean.loc[t, 'right_knee_angle'] = calculate_joint_angle(ra, rk, rh)
        
        # 3. 地面基准计算
        logger.info("\n===== 地面基准计算 =====")
        ground_mean, ground_std, ground_threshold, run_up_end_idx = calculate_ground_reference(
            df_clean, [left_ankle, right_ankle], run_up_ratio=run_up_ratio
        )
        logger.info(f"地面基准高度（h_ground）：{ground_mean:.2f} 像素（助跑前{int(run_up_ratio*100)}%数据）")
        logger.info(f"离地阈值：{ground_threshold:.2f} 像素")
        
        # 4. 起跳判定
        logger.info("\n===== 起跳时刻判定 =====")
        condition1 = df_clean['h_foot_change'] > takeoff_delta1
        condition2 = df_clean['delta_h_foot'] > takeoff_delta2
        condition3 = df_clean['h_foot'] > ground_threshold
        condition4 = df_clean.index > run_up_end_idx
        
        takeoff_candidates = df_clean[condition1 & condition2 & condition3 & condition4]
        if takeoff_candidates.empty:
            relax_delta1 = takeoff_delta1 * 0.8
            relax_delta2 = takeoff_delta2 * 0.8
            logger.warning(f"候选帧为0，放宽条件（delta1={relax_delta1:.1f}, delta2={relax_delta2:.1f}）")
            takeoff_candidates = df_clean[
                ((df_clean['h_foot_change'] > relax_delta1) | (df_clean['delta_h_foot'] > relax_delta2)) &
                condition3 & condition4
            ]
        
        if takeoff_candidates.empty:
            raise ValueError(f"未找到起跳帧 → 建议将takeoff_delta1降至2")
        
        # 速度趋势验证
        valid_takeoff = []
        for idx in takeoff_candidates.index:
            if idx < 1:
                continue
            if df_clean.loc[idx, 'v_y'] > df_clean.loc[idx-1, 'v_y']:
                valid_takeoff.append(idx)
        
        if not valid_takeoff:
            valid_takeoff = takeoff_candidates.index.tolist()
        
        takeoff_idx = min(valid_takeoff)
        takeoff_frame = df_clean.loc[takeoff_idx, '帧号']
        takeoff_time = takeoff_frame / fps
        logger.info(f"最终起跳帧：{takeoff_frame} 帧（{takeoff_time:.2f} 秒）")
        
        # 5. 腾空确认
        logger.info("\n===== 腾空确认 =====")
        post_takeoff_df = df_clean[df_clean.index >= takeoff_idx].copy()
        if len(post_takeoff_df) < 5:
            raise ValueError("起跳后数据不足5帧，无法判定")
        
        airborne_start_idx = None
        # 尝试1：检查起跳后5帧内是否有1帧满足“离地+2像素高度”
        for i in range(min(5, len(post_takeoff_df))):
            current_idx = post_takeoff_df.index[i]
            is_airborne = df_clean.loc[current_idx, 'h_foot'] > ground_threshold
            height_enough = (df_clean.loc[current_idx, 'h_foot'] - ground_mean) >= airborne_height_threshold
            
            if is_airborne and height_enough:
                airborne_start_idx = current_idx
                break
        
        # 尝试2：若失败，忽略高度要求
        if airborne_start_idx is None:
            logger.warning(f"未满足高度要求，仅检查离地（{ground_threshold:.2f}）")
            for i in range(min(5, len(post_takeoff_df))):
                current_idx = post_takeoff_df.index[i]
                if df_clean.loc[current_idx, 'h_foot'] > ground_threshold:
                    airborne_start_idx = current_idx
                    break
        
        # 尝试3：终极方案
        if airborne_start_idx is None:
            logger.warning(f"仍未检测到腾空，将起跳帧{takeoff_frame}视为腾空开始")
            airborne_start_idx = takeoff_idx
        
        airborne_start_frame = df_clean.loc[airborne_start_idx, '帧号']
        airborne_height = df_clean.loc[airborne_start_idx, 'h_foot'] - ground_mean
        logger.info(f"腾空确认：从{airborne_start_frame}帧开始（高度高于地面{airborne_height:.2f}像素）")
        
        # 6. 落地判定
        logger.info("\n===== 落地时刻判定 =====")
        max_flight_frames = int(max_flight_time * fps)
        max_landing_idx = airborne_start_idx + max_flight_frames
        landing_search_df = df_clean[
            (df_clean.index >= airborne_start_idx) & 
            (df_clean.index <= max_landing_idx)
        ].copy()
        
        if len(landing_search_df) == 0:
            max_landing_idx = len(df_clean) - 1
            landing_search_df = df_clean[df_clean.index >= airborne_start_idx].copy()
            logger.warning(f"滞空上限内无数据，扩展至最后一帧")
        
        # 落地条件
        height_condition = landing_search_df['h_foot'] <= (ground_mean + landing_epsilon)
        velocity_condition = landing_search_df['v_y'] < 0
        landing_candidates = landing_search_df[height_condition & velocity_condition]
        
        # 若仍无候选，取最接近阈值的帧
        if landing_candidates.empty:
            closest_idx = landing_search_df['h_foot'].sub(ground_mean + landing_epsilon).abs().idxmin()
            landing_candidates = landing_search_df.loc[[closest_idx]]
            logger.warning(f"无满足条件的落地帧，选择最接近阈值的帧：{df_clean.loc[closest_idx, '帧号']}")
        
        # 加速度验证
        valid_landing = []
        for idx in landing_candidates.index:
            if idx < airborne_start_idx + 1:
                continue
            acc_prev = df_clean.loc[idx-1, 'a_y']
            acc_curr = df_clean.loc[idx, 'a_y']
            if abs(acc_curr - acc_prev) >= 5:
                valid_landing.append(idx)
        
        if not valid_landing:
            valid_landing = landing_candidates.index.tolist()
        
        # 最终落地帧
        landing_idx = min(valid_landing)
        landing_frame = df_clean.loc[landing_idx, '帧号']
        landing_time = landing_frame / fps
        flight_time = landing_time - takeoff_time
        
        # 修正滞空时间
        if flight_time > 1.5:
            fallback_idx = landing_search_df.index[min(10, len(landing_search_df)-1)]
            landing_frame = df_clean.loc[fallback_idx, '帧号']
            landing_time = landing_frame / fps
            flight_time = landing_time - takeoff_time
            logger.warning(f"修正过长滞空：{flight_time:.2f}秒（≤1.5秒）")
        
        # 7. 滞空阶段运动过程分析（新增核心功能）
        logger.info("\n===== 滞空阶段运动过程分析 =====")
        airborne_analysis = analyze_airborne_phase(
            df_clean, takeoff_idx, landing_idx, joint_matches, fps
        )
        if airborne_analysis['status'] == 'success':
            logger.info("滞空阶段描述：\n" + airborne_analysis['description'])
        else:
            logger.warning(airborne_analysis['description'])
        
        # 8. 异常落地检测
        logger.info("\n===== 异常落地检测 =====")
        abnormal_landing = None
        if all([joint_matches['left_wrist'], joint_matches['right_wrist']]):
            # 滑倒检测
            hip_window = df_clean.loc[landing_idx-3:landing_idx+3, joint_matches['hip']]
            if len(hip_window) >= 4:
                hip_drop = hip_window.iloc[:2].mean() > hip_window.iloc[2:].mean()
                wrist_hip = (df_clean.loc[landing_idx, joint_matches['left_wrist']] < df_clean.loc[landing_idx, joint_matches['hip']] or
                             df_clean.loc[landing_idx, joint_matches['right_wrist']] < df_clean.loc[landing_idx, joint_matches['hip']])
                if hip_drop and wrist_hip:
                    abnormal_landing = "滑倒"
        
        # 手部撑地检测
        if all([joint_matches['left_wrist'], joint_matches['right_wrist']]) and not abnormal_landing:
            ankle_height = df_clean.loc[landing_idx, 'h_foot']
            wrist_ankle = (abs(df_clean.loc[landing_idx, joint_matches['left_wrist']] - ankle_height) < 20 or
                          abs(df_clean.loc[landing_idx, joint_matches['right_wrist']] - ankle_height) < 20)
            hip_x = df_clean.loc[landing_idx, joint_matches['hip'].replace('_Y', '_X')]
            wrist_x = (abs(df_clean.loc[landing_idx, joint_matches['left_wrist'].replace('_Y', '_X')] - hip_x) > 70 or
                       abs(df_clean.loc[landing_idx, joint_matches['right_wrist'].replace('_Y', '_X')] - hip_x) > 70)
            if wrist_ankle and wrist_x:
                abnormal_landing = "手部撑地"
        
        logger.info(f"落地状态：{abnormal_landing if abnormal_landing else '正常'}")
        
        # 9. 可视化（包含滞空阶段划分）
        logger.info("\n===== 结果可视化 =====")
        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(16, 15), sharex=True, gridspec_kw={'hspace': 0.3})
        
        # 子图1：脚部高度与起落地
        ax1.plot(df_clean['帧号'], df_clean['h_foot'], color='#1f77b4', linewidth=2.5, label='脚部高度（弱平滑）')
        ax1.axhline(y=ground_mean, color='green', linewidth=2, label=f'地面基准：{ground_mean:.2f}')
        ax1.axhline(y=ground_threshold, color='orange', linestyle='--', linewidth=2, label=f'离地阈值：{ground_threshold:.2f}')
        ax1.axvline(x=takeoff_frame, color='red', linewidth=2.5, label=f'起跳：{takeoff_frame}')
        ax1.axvline(x=landing_frame, color='purple', linewidth=2.5, label=f'落地：{landing_frame}')
        
        # 新增：标注滞空阶段的上升/下降期
        if airborne_analysis['status'] == 'success' and 'apex_idx' in airborne_analysis:
            apex_frame = df_clean.loc[airborne_analysis['apex_idx'], '帧号']
            ax1.axvline(x=apex_frame, color='gold', linestyle='-.', linewidth=2, label=f'最高点：{apex_frame}')
            # 用不同颜色区分上升/下降阶段
            ax1.axvspan(takeoff_frame, apex_frame, color='#8ecae6', alpha=0.3, label='上升阶段')
            ax1.axvspan(apex_frame, landing_frame, color='#219ebc', alpha=0.3, label='下降阶段')
        
        ax1.scatter(takeoff_frame, df_clean.loc[takeoff_idx, 'h_foot'], color='red', s=100, zorder=5)
        ax1.scatter(landing_frame, df_clean.loc[landing_idx, 'h_foot'], color='purple', s=100, zorder=5)
        if airborne_analysis['status'] == 'success' and 'apex_idx' in airborne_analysis:
            ax1.scatter(apex_frame, df_clean.loc[airborne_analysis['apex_idx'], 'h_foot'], 
                       color='gold', s=100, marker='*', zorder=5)
        ax1.set_ylabel('高度（像素）', fontsize=12)
        ax1.set_title(f'跳远轨迹（滞空时间：{flight_time:.2f}秒）', fontsize=14, fontweight='bold')
        ax1.legend(loc='upper left')
        ax1.grid(alpha=0.3)
        
        # 子图2：质心速度
        ax2.plot(df_clean['帧号'], df_clean['v_y'], color='#ff7f0e', linewidth=2.5, label='质心速度')
        ax2.axhline(y=0, color='black', linewidth=1.5, alpha=0.5)
        ax2.axvline(x=takeoff_frame, color='red', linewidth=2.5)
        ax2.axvline(x=landing_frame, color='purple', linewidth=2.5)
        if airborne_analysis['status'] == 'success' and 'apex_idx' in airborne_analysis:
            ax2.axvline(x=apex_frame, color='gold', linestyle='-.', linewidth=2)
            ax2.axvspan(takeoff_frame, apex_frame, color='#8ecae6', alpha=0.3)
            ax2.axvspan(apex_frame, landing_frame, color='#219ebc', alpha=0.3)
        
        ax2.scatter(landing_frame, df_clean.loc[landing_idx, 'v_y'], color='purple', s=100, zorder=5)
        ax2.set_ylabel('速度（像素/秒）', fontsize=12)
        ax2.set_title('质心速度变化（上升期速度≥0，下降期速度<0）', fontsize=14, fontweight='bold')
        ax2.legend(loc='upper left')
        ax2.grid(alpha=0.3)
        
        # 子图3：髋关节与手腕高度变化
        ax3.plot(df_clean['帧号'], df_clean[joint_matches['hip']], color='#2ca02c', linewidth=3, 
                 label=f'髋关节（{joint_matches["hip"]}）')
        if joint_matches['left_wrist']:
            ax3.plot(df_clean['帧号'], df_clean[joint_matches['left_wrist']], color='#d62728', linewidth=2.5, 
                     label=f'左手腕（{joint_matches["left_wrist"]}）')
        if joint_matches['right_wrist']:
            ax3.plot(df_clean['帧号'], df_clean[joint_matches['right_wrist']], color='#9467bd', linewidth=2.5, 
                     label=f'右手腕（{joint_matches["right_wrist"]}）')
        
        ax3.axvline(x=takeoff_frame, color='red', linewidth=2.5, label=f'起跳：{takeoff_frame}')
        ax3.axvline(x=landing_frame, color='purple', linewidth=2.5, label=f'落地：{landing_frame}')
        if airborne_analysis['status'] == 'success' and 'apex_idx' in airborne_analysis:
            ax3.axvline(x=apex_frame, color='gold', linestyle='-.', linewidth=2, label=f'最高点：{apex_frame}')
            ax3.axvspan(takeoff_frame, apex_frame, color='#8ecae6', alpha=0.3, label='上升阶段')
            ax3.axvspan(apex_frame, landing_frame, color='#219ebc', alpha=0.3, label='下降阶段')
        
        landing_hip_height = df_clean.loc[landing_idx, joint_matches['hip']]
        ax3.axhline(y=landing_hip_height, color='#2ca02c', linestyle='--', alpha=0.7, 
                    label=f'落地时髋高：{landing_hip_height:.2f}')
        ax3.set_xlabel('帧号', fontsize=12)
        ax3.set_ylabel('高度（像素）', fontsize=12)
        ax3.set_title('髋关节与手腕高度变化（异常落地检测参考）', fontsize=14, fontweight='bold')
        ax3.legend(loc='upper left')
        ax3.grid(alpha=0.3)
        
        plt.subplots_adjust(left=0.06, right=0.94, top=0.95, bottom=0.05)
        plt.show()
        
        # 10. 保存结果（包含滞空分析）
        if save_results:
            result_file = file_path.parent / f"{file_path.stem}_最终结果.xlsx"
            with pd.ExcelWriter(result_file, engine='openpyxl') as writer:
                # 核心结果
                core_result = pd.DataFrame([{
                    '分析时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                    '数据文件': file_path.name,
                    '起跳帧': takeoff_frame,
                    '起跳时间(秒)': takeoff_time,
                    '落地帧': landing_frame,
                    '落地时间(秒)': landing_time,
                    '滞空时间(秒)': flight_time,
                    '上升时间(秒)': airborne_analysis['key_params'].get('上升时间(秒)') if airborne_analysis['status'] == 'success' else None,
                    '下降时间(秒)': airborne_analysis['key_params'].get('下降时间(秒)') if airborne_analysis['status'] == 'success' else None,
                    '最高点帧': airborne_analysis['key_params'].get('最高点帧') if airborne_analysis['status'] == 'success' else None,
                    '最大上升高度(像素)': airborne_analysis['key_params'].get('最大上升高度(像素)') if airborne_analysis['status'] == 'success' else None,
                    '地面基准(像素)': ground_mean,
                    '离地阈值(像素)': ground_threshold,
                    '异常落地类型': abnormal_landing if abnormal_landing else '正常'
                }])
                core_result.to_excel(writer, sheet_name='核心结果', index=False)
                
                # 滞空阶段描述
                if airborne_analysis['status'] == 'success':
                    desc_df = pd.DataFrame({
                        '滞空阶段运动描述': airborne_analysis['description'].split('\n')
                    })
                    desc_df.to_excel(writer, sheet_name='滞空阶段分析', index=False)
                
                # 关键帧数据
                key_frames = df_clean[
                    (df_clean['帧号'] >= takeoff_frame - 5) & 
                    (df_clean['帧号'] <= landing_frame + 5)
                ][['帧号', 'h_foot', 'h_foot_change', 'v_y', 'a_y', 
                   joint_matches['hip'], joint_matches.get('left_wrist'), joint_matches.get('right_wrist')]]
                key_frames.to_excel(writer, sheet_name='关键帧数据', index=False)
            
            logger.info(f"\n结果保存至：{result_file.resolve()}")
        
        end_time = datetime.now()
        logger.info(f"\n=== 分析完成（耗时：{(end_time - start_time).total_seconds():.2f}秒）===")
        return {
            'status': 'success',
            'takeoff_frame': takeoff_frame,
            'landing_frame': landing_frame,
            'flight_time': flight_time,
            'airborne_description': airborne_analysis.get('description', ''),
            'result_file': str(result_file)
        }
    
    except Exception as e:
        logger.error(f"分析失败：{str(e)}", exc_info=True)
        return {'status': 'error', 'error_msg': str(e)}


# 主函数调用
if __name__ == "__main__":
    result = analyze_long_jump(
        file_path=r'C:\Users\lianxiang\Desktop\E题\附件\附件1\运动者2的跳远位置信息.xlsx',
        target_sheet='Sheet1',
        takeoff_delta1=3,
        takeoff_delta2=6,
        fps=30,
        save_results=True,
        debug=True
    )
    if result['status'] == 'success':
        print(f"\n✅ 分析成功！")
        print(f"   起跳帧：{result['takeoff_frame']} 帧")
        print(f"   落地帧：{result['landing_frame']} 帧")
        print(f"   滞空时间：{result['flight_time']:.2f} 秒")
        print(f"\n📝 滞空阶段运动描述：")
        print(result['airborne_description'])
        print(f"\n📄 结果文件：{result['result_file']}")
    else:
        print(f"\n❌ 分析失败：{result['error_msg']}")
